---
title: 工厂模式
description: Factory Pattern - 一种创建型设计模式，提供了一种将对象的实例化过程封装在工厂类中的方式。包括简单工厂、工厂方法和抽象工厂三种实现方式。
sidebar_position: 2
---

# 工厂模式

## 模式概述

### 定义
工厂模式是一种创建型设计模式，它提供了一种将对象的实例化过程封装在工厂类中的方式。根据实现方式的不同，工厂模式可以分为简单工厂、工厂方法和抽象工厂三种类型。

在工厂模式中，创建对象时不会对客户端暴露创建逻辑，并且是通过使用一个共同的接口来指向新创建的对象。

简单工厂模式是工厂方法模式的一种特例，不属于 GOF 的 23 中经典设计模式


### 通俗理解
想象你去一家披萨店点披萨。你不需要自己去厨房制作披萨，只需要告诉服务员你想要什么口味的披萨（比如海鲜披萨或者水果披萨），服务员会把你的需求转达给厨房（工厂），厨房就会制作相应的披萨。这就是工厂模式的基本思想 - 你只需要告诉工厂你要什么，工厂负责创建对应的产品给你。

### 为什么要用工厂模式？
假设你正在开发一个应用程序，需要处理不同类型的文件（JSON、XML、YAML等）。如果直接在代码中使用new来创建对象：

```java
// 不好的做法
if (fileType.equals("json")) {
    parser = new JsonParser();
} else if (fileType.equals("xml")) {
    parser = new XmlParser();
} else if (fileType.equals("yaml")) {
    parser = new YamlParser();
}
```

这样的代码有以下问题：
1. 创建代码散布在各处，难以维护
2. 每次添加新的文件类型都要修改代码
3. 代码重复，违反了DRY原则

使用工厂模式后：
```java
// 好的做法
Parser parser = ParserFactory.createParser(fileType);
```

### 生活中的例子
1. **餐厅点餐**
   - 顾客（客户端）不需要知道厨房（工厂）如何制作食物
   - 服务员（工厂接口）接收订单
   - 厨师（具体工厂）负责制作具体的菜品

2. **汽车制造**
   - 4S店（客户端）向工厂订购汽车
   - 汽车制造厂（工厂）负责生产具体型号的汽车
   - 不同型号的汽车由不同的生产线（具体工厂）生产

## 工厂模式分类与实现

### 工厂模式的演进

#### 1. 简单工厂（Simple Factory）
简单工厂模式不是一种设计模式，反而更像是一种编程习惯（面向抽象（接口）编程 ）。简单工厂模式又叫做静态工厂方法模式（static Factory Method pattern）,它是通过静态方法接受不同的参数来返回不同的实例对象。

实现方式：定义一个工厂类，根据传入的参数不同返回不同的实例，被创建的实例具有共同的父类或接口。

适用场景：
1. 需要创建的对象较少
2. 客户端不关心对象的创建过程。

简单工厂包含如下角色：
* 抽象产品：定义了产品的规范，描述了产品的主要特性和功能。
* 具体产品：实现或者继承抽象产品的子类。
* 提供了创建产品的方法，调用者通过该方法来获取产品。

![](https://wkq-img.oss-cn-chengdu.aliyuncs.com/site/20250727190105.png)


![](https://wkq-img.oss-cn-chengdu.aliyuncs.com/site/20250727220406.png)


```java
// 简单工厂实现
public class RuleConfigParserFactory {
    // 这个方法就像一个万能工人，根据需求制作不同的产品
    public static IRuleConfigParser createParser(String configFormat) {
        // 创建解析器对象
        IRuleConfigParser parser = null;
        // 根据配置文件格式选择对应的解析器
        if ("json".equalsIgnoreCase(configFormat)) {
            parser = new JsonRuleConfigParser();  // 创建JSON解析器
        } else if ("xml".equalsIgnoreCase(configFormat)) {
            parser = new XmlRuleConfigParser();   // 创建XML解析器
        }
        return parser;
    }
}
```
**优点：**
- 简单直观，容易理解
- 适合产品种类较少的情况
- 封装了创建对象的过程，可以通过参数直接获取对象，把对象的创建和业务逻辑分开，避免了可能会修改客户代码的问题。
- 如果要修改新产品，直接修改工厂类，不需要在源代码中进行修改，降低了客户端修改代码的可能性，更加容易扩展。

**缺点：**
- 如果要添加新的产品，还是需要修改工厂类的代码，违背了开闭原则。
- 工厂类职责过重，违反单一职责原则



**优化版本：使用工厂类缓存**

```java
public class RuleConfigParserFactory {
    private static final Map<String, IRuleConfigParser> cachedParsers = new HashMap<>();

    static {
        cachedParsers.put("json", new JsonRuleConfigParser());
        cachedParsers.put("xml", new XmlRuleConfigParser());
        cachedParsers.put("yaml", new YamlRuleConfigParser());
        cachedParsers.put("properties", new PropertiesRuleConfigParser());
    }

    public static IRuleConfigParser createParser(String configFormat) {
        if (configFormat == null || configFormat.isEmpty()) {
            return null;
        }
        return cachedParsers.get(configFormat.toLowerCase());
    }
}
```

#### 2. 工厂方法（Factory Method）
:::tip
简单工厂模式存在一个明显的缺点，就是在增加新产品的时候，还是需要修改工厂类的代码的，这就违背了开闭原则。
:::

**定义一个用于创建对象的接口，让子类决定实例化那个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。**

工厂方法模式的目的很简单，就是 **封装对象创建的过程，提升创建对象方法的可复用性**。
工厂方法模式的主要角色：
* 抽象工厂：提供了创建产品的接口，调用者通过它访问具体工厂的工厂方法。
* 具体工厂：主要是实现抽象工厂中的抽象方法，完成具体产品的创建。
* 抽象产品： 定义了产品的规范，描述了产品的主要特性和功能。
* 具体产品：实现了抽象产品角色所定义的接口，由具体的工厂来创建，它同具体工厂之间一一对应。

![](https://wkq-img.oss-cn-chengdu.aliyuncs.com/site/20250727223721.png)

![](https://wkq-img.oss-cn-chengdu.aliyuncs.com/site/20250727233335.png)

当对象的创建逻辑比较复杂，不只是简单的new一下就可以，而是要组合其他类对象，做各种初始化操作的时候，推荐使用工厂方法模式。
这种模式更像是一个工厂集团，每个工厂专门负责生产一种产品。
```java
// 工厂接口 - 定义了所有工厂都必须实现的方法
public interface IRuleConfigParserFactory {
    IRuleConfigParser createParser();  // 创建解析器的方法
}

// JSON解析器工厂 - 专门负责创建JSON解析器
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
        return new JsonRuleConfigParser();  // 创建JSON解析器
    }
}

public class XmlConfigParserFactory implements IConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
        return new XmlRuleConfigParser();
    }
}
```

**为什么这样设计更好？**
1. 每个工厂类只负责创建一种产品，职责单一
2. 添加新产品只需要添加新的工厂类，不需要修改现有代码
3. 符合开闭原则（对扩展开放，对修改关闭）
```
// 工厂的工厂
public class ConfigParserFactoryMap {
    private static final Map<String, IConfigParserFactory> cachedFactories = new HashMap<>();

    static {
        cachedFactories.put("json", new JsonConfigParserFactory());
        cachedFactories.put("xml", new XmlConfigParserFactory());
    }

    public static IConfigParserFactory getParserFactory(String type) {
        if (type == null || type.isEmpty()) {
            return null;
        }
        return cachedFactories.get(type.toLowerCase());
    }
}
```


**优点：**
- 用户只需要知道具体工厂的名字，就可以获取到想要的产品，不需要关注产品的创建过程。
- 在系统新增加产品的时候，只需要添加具体产品类和对应的具体工厂，不需要对原工厂进行修改，满足了开闭原则。

**缺点：**
- 每增加一个产品，就需要一个具体的产品类和对应的工厂类，这样会增加系统的复杂度。


#### 3. 抽象工厂（Abstract Factory）
:::tip
抽象工厂模式比工厂方法模式的抽象程度更高，在工厂方法模式中每一个具体工厂只需要生产一种具体产品，但是在抽象工厂模式中一个具体工厂可以生产一组相关的具体产品，这样一组产品被称为产品族，产品族中的每一个产品成分都属于某一个继承等级结构。
:::


**产品等级结构与产品组**
* 产品等级结构：产品等级结构即产品的继承结构，如一个抽象类是电视机，其子类有海尔电视机、海信电视机、TCL 电视机，则抽象电视机与具体电视机之间构成了一个产品等级结构，抽象电视机是父类，而具体品牌的电视机是其子类。
* 产品族：**在抽象工厂模式中，产品族是指由同一个工厂生产的，位于不同产品等级结构中的一组产品**，如海尔电器工厂生产的海尔电视机、海尔电冰箱、海尔电视机位于电视机产品等级结构中，海尔电冰箱位于电冰箱产品等级结构中。
![](https://wkq-img.oss-cn-chengdu.aliyuncs.com/site/20250728215056.png)
提供一个创建一系列相关或相互依赖对象的接口，而无需指定它们具体的类。
抽象工厂为创建一组对象提供了解决方案，与工厂方法模式相比，**抽象工厂模式中的具体工厂不只是创建一种产品，而是负责创建一个产品族**，这种模式就像是一个超级工厂，可以生产多个产品系列。比如，一个工厂不仅可以生产轮胎，还可以生产发动机、方向盘等。

![](https://wkq-img.oss-cn-chengdu.aliyuncs.com/site/20250728215622.png)

![](https://wkq-img.oss-cn-chengdu.aliyuncs.com/site/20250728225643.png)
```
// 抽象产品等级接口
public interface IRuleConfigParser {
    void parseRule(String ruleConfig);
}

public interface ISystemConfigParser {
    void parseSystem(String systemConfig);
}

// 具体产品实现
public class JsonRuleConfigParser implements IRuleConfigParser {
    @Override
    public void parseRule(String ruleConfig) {
        System.out.println("解析JSON规则配置: " + ruleConfig);
    }
}

public class JsonSystemConfigParser implements ISystemConfigParser {
    @Override
    public void parseSystem(String systemConfig) {
        System.out.println("解析JSON系统配置: " + systemConfig);
    }
}

public class XmlRuleConfigParser implements IRuleConfigParser {
    @Override
    public void parseRule(String ruleConfig) {
        System.out.println("解析XML规则配置: " + ruleConfig);
    }
}

public class XmlSystemConfigParser implements ISystemConfigParser {
    @Override
    public void parseSystem(String systemConfig) {
        System.out.println("解析XML系统配置: " + systemConfig);
    }
}

// 抽象工厂接口
public interface IConfigParserFactory {
    IRuleConfigParser createRuleParser();
    ISystemConfigParser createSystemParser();
}

// 具体工厂实现
public class JsonConfigParserFactory implements IConfigParserFactory {
    @Override
    public IRuleConfigParser createRuleParser() {
        return new JsonRuleConfigParser();
    }

    @Override
    public ISystemConfigParser createSystemParser() {
        return new JsonSystemConfigParser();
    }
}

public class XmlConfigParserFactory implements IConfigParserFactory {
    @Override
    public IRuleConfigParser createRuleParser() {
        return new XmlRuleConfigParser();
    }

    @Override
    public ISystemConfigParser createSystemParser() {
        return new XmlSystemConfigParser();
    }
}
```
从上面的代码实现中可以看出，抽象工厂模式向使用（客户）方隐藏了下列变化：
* 程序所支持的实例集合（具体工厂）的数目。
* 当前使用的是集合中的哪一个实例；
* 在任意时刻被实例化的具体类型；


在理解抽象工厂原理时，要 考虑 如何找到某一类产品的争取共性功能 。

:::tip
JDBC 的实现就是抽象工厂模式，无论使用什么样的数据库，只要数据库支持 JDBC ，就能对数据库进行读写操作。
还有的示例是日志收集工厂。
:::
**优点**
* 对于不同产品系列有比较多共性特征时，可以使用抽象工厂模式，有助于提升组件的复用性。
* 当需要提升代码的扩展性并降低维护成本时，把对象的创建和使用过程分开，能有效地将代码统一到一个级别上。
* 解决跨平台带来的兼容性问题。

**缺点**
* 增加新的产品等级结构麻烦，需要对原有结构进行较大的修改，甚至需要修改抽象层代码，这显然会代码较大的不变，违背了开闭原则。

### 工厂模式的应用场景

1. **封装变化**：
   - 创建对象的过程可能会随时发生变化
   - 需要屏蔽对象创建的细节

2. **代码复用**：
   - 创建对象的代码很复杂
   - 需要在多个地方创建同类对象

3. **隔离复杂性**：
   - 将对象的创建和使用分离
   - 降低代码的耦合度

4. **控制复杂度**：
   - 当系统中的对象种类较多时
   - 通过工厂模式进行分类管理

### 如何选择工厂模式？

1. **使用简单工厂**：
   - 工厂类负责创建的对象比较少
   - 客户端只需要传入工厂类的参数，对于如何创建对象不关心

2. **使用工厂方法**：
   - 客户端需要决定实例化哪一个工厂来实现运行时的动态变化
   - 多个工厂类对应多个产品类，每个工厂创建一种产品

3. **使用抽象工厂**：
   - 需要创建的产品族具有相同的约束
   - 产品等级结构稳定，设计完成之后不会向系统中增加新的产品等级结构

### 工厂模式的注意事项

1. **性能考虑**
   - 使用简单工厂时，考虑对象缓存
   - 工厂方法中，可以使用工厂的工厂来缓存工厂实例

2. **扩展性设计**
   - 尽量使用配置文件而不是硬编码来管理工厂类
   - 考虑使用反射机制来创建对象

3. **异常处理**
   - 处理创建对象过程中的异常
   - 提供合理的默认值或降级策略

## 实际应用示例

### 1. 日志记录器示例
假设我们需要实现一个可以将日志输出到不同地方（控制台、文件、数据库）的系统：

```
// 日志记录器接口
public interface Logger {
    void log(String message);
}

// 控制台日志记录器
public class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("控制台日志: " + message);
    }
}

// 文件日志记录器
public class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("文件日志: " + message);
        // 实际中这里会写入文件
    }
}

// 日志记录器工厂接口
public interface LoggerFactory {
    Logger createLogger();
}

// 控制台日志记录器工厂
public class ConsoleLoggerFactory implements LoggerFactory {
    @Override
    public Logger createLogger() {
        return new ConsoleLogger();
    }
}

// 使用示例
public class LoggerTest {
    public static void main(String[] args) {
        LoggerFactory factory = new ConsoleLoggerFactory();
        Logger logger = factory.createLogger();
        logger.log("这是一条测试日志");  // 输出：控制台日志: 这是一条测试日志
    }
}
```

### 2. 数据库连接示例
不同数据库（MySQL、Oracle、SQLite）的连接创建：

```
// 数据库连接接口
public interface DBConnection {
    void connect();
}

// MySQL连接实现
public class MySQLConnection implements DBConnection {
    @Override
    public void connect() {
        System.out.println("连接到MySQL数据库");
    }
}

// Oracle连接实现
public class OracleConnection implements DBConnection {
    @Override
    public void connect() {
        System.out.println("连接到Oracle数据库");
    }
}

// 数据库连接工厂接口
public interface DBConnectionFactory {
    DBConnection createConnection();
}

// MySQL连接工厂
public class MySQLConnectionFactory implements DBConnectionFactory {
    @Override
    public DBConnection createConnection() {
        return new MySQLConnection();
    }
}

// 使用示例
public class DBTest {
    public static void main(String[] args) {
        DBConnectionFactory factory = new MySQLConnectionFactory();
        DBConnection connection = factory.createConnection();
        connection.connect();  // 输出：连接到MySQL数据库
    }
}
```

## 最佳实践

### 1. 如何选择合适的工厂模式？
1. **使用简单工厂**
   - 当你的产品很少（2-3种）
   - 不经常需要添加新产品
   - 例如：只需要支持JSON和XML两种格式的解析器

2. **使用工厂方法**
   - 当你需要经常添加新的产品
   - 产品的创建逻辑比较复杂
   - 例如：需要支持多种数据库连接

3. **使用抽象工厂**
   - 当你需要创建一系列相关的产品
   - 这些产品是成套出现的
   - 例如：需要同时创建数据库连接和对应的数据库命令

### 2. 编码建议
1. **命名规范**
   ```java
   // 好的命名
   public class MySQLConnectionFactory { ... }
   public class JsonParserFactory { ... }

   // 不好的命名
   public class MySQLFactory { ... }  // 不够具体
   public class Factory1 { ... }      // 完全不具体
   ```

2. **异常处理**
   ```java
   public class ConfigParserFactory {
       public static IConfigParser createParser(String type) {
           try {
               if (type == null || type.isEmpty()) {
                   throw new IllegalArgumentException("配置类型不能为空");
               }
               // 创建解析器
           } catch (Exception e) {
               throw new ParserCreationException("创建解析器失败", e);
           }
       }
   }
   ```

## 高级应用：依赖注入框架实现

### 与工厂模式的关系

1. **从工厂模式到DI容器的演进**
   - 简单工厂：解决了对象创建的问题
   - 工厂方法：增加了扩展性
   - 抽象工厂：支持产品族的创建
   - DI容器：在工厂模式基础上增加了依赖管理和生命周期控制

2. **工厂职责的扩展**
   ```java
   // 传统工厂模式
   public class DatabaseFactory {
       public static Database createDatabase(String type) {
           if ("MySQL".equals(type)) {
               return new MySQLDatabase();
           }
           // ...
       }
   }

   // DI容器方式
   @Configuration
   public class DatabaseConfig {
       @Bean
       public Database database(DbProperties props) {  // 自动注入依赖
           Database db = DatabaseFactory.create(props.getType());
           db.setProperties(props);
           return db;
       }
   }
   ```

3. **配置方式的演进**
   - 工厂模式：通常使用代码配置
   - DI容器：支持多种配置方式
     ```xml
     <beans>
       <bean id="rateLimiter" class="com.example.RateLimiter">
         <constructor-arg ref="redisCounter"/>
       </bean>
     </beans>
     ```

### DI容器的核心功能

1. **配置解析**
   - 通过配置文件(如XML)声明需要创建的对象
   - 指定对象之间的依赖关系
   - 配置对象的作用域(单例/原型)和初始化参数

2. **对象创建**
   - 使用反射机制动态加载类、创建对象
   - 将所有类对象的创建放到一个工厂类中完成
   - 支持构造函数注入和属性注入

3. **生命周期管理**
   - 管理单例对象和原型对象
   - 支持懒加载
   - 支持初始化和销毁方法的配置

### 实现要点

1. **工厂模式的应用**
   - 使用工厂方法创建Bean实例
   - 利用抽象工厂管理不同类型的Bean创建
   - 结合简单工厂实现单例Bean的缓存

2. **核心组件**
   - BeanFactory：核心工厂接口，定义了Bean的获取方法
   - ApplicationContext：高级工厂接口，扩展了基础工厂功能
   - BeanDefinition：Bean的配置信息，类似于工厂的产品说明书

3. **关键实现细节**
   - Bean的创建使用工厂模式的变体
   - 单例Bean的管理借鉴了简单工厂的缓存思想
   - 原型Bean的创建类似于工厂方法模式

### 注意事项

1. **工厂模式的局限性**
   - 传统工厂模式难以处理复杂的依赖关系
   - 工厂方法模式不适合管理对象的完整生命周期
   - 抽象工厂模式难以应对动态的对象创建需求

2. **DI容器的解决方案**
   - 通过依赖注入解决复杂依赖问题
   - 提供完整的生命周期管理
   - 支持动态Bean定义和创建

3. **最佳实践**
   - 合理使用工厂模式和DI容器
   - 简单对象直接使用工厂模式
   - 复杂依赖关系使用DI容器



